/**全局的弹窗控制  
 * 支持弹窗 打开，关闭，销毁，预加载
 */
type DialogName = "AddGame" | "BlockDesc" | "Broke" | "DailyReward" | "DicePoints" | "DiscardCard" |
    "Escape" | "FindPlayer" | "GetBox" | "Investasi" | "LevelUp" | "Loan" |
    "Lose" | "LuckDraw" | "Mail" | "MailInfo" | "Match" | "MoneyChange" |
    "MyTurn" | "OnlineReward" | "Rank" | "RankReward" | "Reward" | "RewardTips" |
    "Skin" | "Sort" | "TakeOver" | "TaskAchivement" | "Trust" | "Watch" |
    "WhetherCollect" | "Win";
let GDialog = new class {

    /**弹窗打开时候的动画类型 */
    OpenAnim = {
        /**没有弹窗动画 */
        None: "OpenAnimNone",
        /**缩放动画，弹窗从最小值变到原来大小 */
        Scale: "OpenAnimScale",
    }

    /**弹窗关闭时候的动画类型 */
    CloseAnim = {
        /**没有关闭弹窗动画 */
        None: "CloseAnimNone",
        /**缩放动画，弹窗从原来大小缩放到最小值然后移除 */
        Scale: "CloseAnimScale",
    }

    /**保存所有弹窗预制的根目录，相对"resources/"目录  "Dialog"*/
    private _DialogPath = "Dialog";
    /**弹窗的父节点，默认为当前场景  cc.director.getScene() */
    private _DialogParent = cc.director.getScene();
    /**弹窗的默认位置，默认场景中央 */
    private _DialogPosition = cc.v2(cc.winSize.width / 2, cc.winSize.height / 2);
    /**弹窗打开时候的默认动画 默认缩放动画 */
    private _OpenAnimDefault = this.OpenAnim.Scale;
    /**弹窗关闭时候的默认动画 默认缩放动画 */
    private _CloseAnimDefault = this.CloseAnim.Scale;
    /**弹窗预制加载完成后设置更新数据的函数名 "setData" */
    private _InitFunctionName = "setData";
    /**动画作用的节点的名字 "all" */
    private _AnimNodeName = "all";
    /**关闭节点的相对路径  "all/close" */
    private _ClosePath = "all/close";


    /**保存所有弹窗节点的map */
    private _allDialog: Map<string, cc.Node> = new Map();

    /**打开缩放动画，没有动画 */
    private OpenAnimNone(node: cc.Node) {
        let js: Dialog<any> = node.getComponent(node.name);
        js.onOpen();
    }

    /**打开缩放动画，弹窗从最小值变到原来大小 */
    private OpenAnimScale(node: cc.Node) {
        let target = node.getChildByName(this._AnimNodeName);
        cc.tween(node)
            .call(() => { node.opacity = 0; })
            .delay(0)
            .call(() => {
                node.opacity = 255;
                target.setScale(0.01);
                cc.tween(target).to(0.3, { scale: 1 }, { easing: "backOut" })
                    .call(() => {
                        let js: Dialog<any> = node.getComponent(node.name);
                        js.onOpen();
                    })
                    .start();
            })
            .start();
    }


    /**打开缩放动画，没有动画 */
    private CloseAnimNone(node: cc.Node) {
        let js: Dialog<any> = node.getComponent(node.name);
        js.onClose();
        node.destroy();
        this._allDialog.delete(node.name);
    }

    /**关闭缩放动画，弹窗从原来大小缩放到最小值然后移除 */
    private CloseAnimScale(node: cc.Node) {
        let target = node.getChildByName(this._AnimNodeName);
        if (target.getNumberOfRunningActions() == 0) {
            cc.tween(target)
                .to(0.3, { scale: 0.01 }, { easing: "backIn" })
                .call(() => {
                    let js: Dialog<any> = node.getComponent(node.name);
                    js.onClose();
                    node.destroy();
                    this._allDialog.delete(node.name);
                })
                .start();
        }
    }

    /**初始化设置Dialog的默认参数，一般都用默认的默认参数，  
     * - ***可不调用此函数***  
     * @param optional 属性参数说明如下：
     * @param optional.DialogPath 保存所有弹窗预制的根目录，相对"resources/"目录  默认为"Dialog"  
     * @param optional.DialogParent 弹窗的父节点，默认为当前场景  cc.director.getScene()   
     * @param optional.DialogPosition 弹窗的默认位置，默认场景中央     
     * @param optional.OpenAnimDefault 弹窗打开时候的默认动画 默认为 GDialog.OpenAnim.Scale 缩放动画  
     * @param optional.CloseAnimDefault 弹窗关闭时候的默认动画 默认为 GDialog.CloseAnim.Scale 缩放动画   
     * @param optional.InitFunctionName 弹窗预制加载完成后设置更新数据的函数名，默认为 "setData"  
     * @param optional.AnimNodeName 动画作用的节点的名字 默认为"all"   
     * @param optional.ClosePath 关闭节点的相对路径  "all/close"   
     * @example
     * GDialog.init({ OpenAnimDefault: GDialog.OpenAnim.Scale, CloseAnimDefault: GDialog.CloseAnim.Scale }); //设置了默认的弹窗打开和关闭动画
     */
    init(optional: {
        DialogPath?: string, DialogParent?: cc.Node, DialogPosition?: cc.Vec2,
        OpenAnimDefault?: string, CloseAnimDefault?: string, InitFunctionName?: string, AnimNodeName?: string, ClosePath?: string,
    } = {}) {
        //设置了默认参数的替换默认的默认参数
        for (let key of Object.keys(optional)) {
            if (typeof this["_" + key] != "undefined") {
                this["_" + key] = optional[key];
            }
        }
    }

    /**打开弹窗  
     * @param name 弹窗预制的名字  
     * @param optional 属性参数说明如下：  
     * @param optional.openAnim 弹出打开时候的采用的动画类型，不设置即为默认的动画类型  
     * @param optional.parent dialog的父节点   
     * @param optional.position dialog的位置  
     * @param optional.setData 传递给预制上挂载的脚本的设置数据函数的参数  
     * @example
     *  
     */
    async open(name: DialogName, optional: { openAnim?: string, parent?: cc.Node, position?: cc.Vec2, setData?: any } = {}): Promise<cc.Node> {
        this._DialogParent = cc.isValid(this._DialogParent) ? this._DialogParent : cc.director.getScene();
        let { openAnim, setData, parent, position } = optional;
        openAnim = openAnim || this._OpenAnimDefault;
        parent = parent || this._DialogParent;
        position = position || this._DialogPosition;
        return new Promise((resolve, reject) => {
            let dialog = this._allDialog.get(name);
            if (dialog && cc.isValid(dialog)) {//已经加载过了
                this._open(dialog, openAnim, parent, position, setData);
                resolve(dialog);
            } else {//没加载过的预制dialog
                cc.loader.loadRes(`${this._DialogPath}/${name}`, cc.Prefab, (err, prefab: cc.Prefab) => {
                    if (err) {
                        console.error(err.message || err);
                        reject(err);
                        return;
                    }
                    //生成新的dialog
                    dialog = cc.instantiate(prefab);
                    this._allDialog.set(name, dialog);
                    this._open(dialog, openAnim, parent, position, setData);
                    resolve(dialog);
                });
            }
        });
    }

    /**打开dialog后的处理逻辑 */
    private _open(dialog: cc.Node, openAnim: string, parent: cc.Node, position: cc.Vec2, setData: any) {
        dialog.parent = cc.isValid(parent) ? parent : cc.director.getScene();
        dialog.position = position;
        //为close节点自动添加按钮响应关闭函数
        if (!dialog["__isAddClose"]) {
            dialog["__isAddClose"] = true;
            let close = cc.find(this._ClosePath, dialog);
            if (close && !close.getComponent(cc.Button)) {
                //添加button，缩放过渡动画
                let button = close.addComponent(cc.Button);
                button.transition = cc.Button.Transition.SCALE;
                let event = new cc.Component.EventHandler();
                event.target = dialog;
                event.component = dialog.name;
                event.handler = "clickClose";
                if (!dialog.getComponent(dialog.name).clickClose) {
                    dialog.getComponent(dialog.name).clickClose = function () {
                        GDialog.close(this.node);
                    };
                }
                button.clickEvents.push(event);
            }
        }
        let widget = dialog.getComponent(cc.Widget);
        if (widget) {
            widget.updateAlignment();
        }
        //执行顺序 onload setData start
        if (setData) {
            let script = dialog.getComponent(dialog.name);
            if (script && script[this._InitFunctionName]) {
                if (script.__preload && script._isOnLoadCalled == 0) script.__preload();
                script[this._InitFunctionName](setData);
            } else {
                console.warn(`没有${this._InitFunctionName}函数`, dialog.name);
            }
        }
        this[openAnim](dialog);
    }

    /**关闭弹窗  
     * - 此接口只是把节点从场景中移除，并不会销毁节点    
     * @param target 弹窗的名字 或者 节点   
     * @example
     * 
     */
    close(target: DialogName | cc.Node, optional: { closeAnim?: string } = {}) {
        let { closeAnim } = optional;
        closeAnim = closeAnim || this._CloseAnimDefault;
        let dialog: cc.Node = null;
        if (typeof target == "string") {
            dialog = this._allDialog.get(target);
        } else {
            dialog = target;
        }
        this[closeAnim](dialog);
    }

    /**销毁弹窗  
     * - 此接口可以直接销毁一个弹窗节点，不会播放关闭动画
     * @param target 弹窗的名字 或者 节点   
     * @example
     * 
     */
    destroy(target: DialogName | cc.Node) {
        let dialog: cc.Node = null;
        let name = "";
        if (typeof target == "string") {
            dialog = this._allDialog.get(target);
            name = target;
        } else {
            dialog = target;
            name = target.name;
        }
        dialog.destroy();
        this._allDialog.delete(name);
    }

    /**销毁所有弹窗   
     * @example
     * GDialog.destroyAll();
     */
    destroyAll() {
        this._allDialog.forEach((dialog: cc.Node, name: string) => {
            dialog.destroy();
            this._allDialog.delete(name);
        });
    }

    /**预加载一个弹窗  
     * @param name 弹窗预制的名字  
     * @example
     * 
     */
    preLoad(name: DialogName) {
        cc.loader.loadRes(`${this._DialogPath}/${name}`, cc.Prefab, (err, prefab: cc.Prefab) => {
            if (err) {
                console.error(err.message || err);
                return;
            }
            //生成新的dialog
            let dialog = cc.instantiate(prefab);
            this._allDialog.set(name, dialog);
        });
    }

}();
window["GDialog"] = GDialog;